home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Online / SpeakFreely / src / launch.c < prev    next >
C/C++ Source or Header  |  2000-05-18  |  12KB  |  447 lines

  1. /*
  2.  
  3.             Speak Freely for Unix
  4.             Launcher for Mike and Speaker
  5.  
  6.      Designed and implemented in September of 1999 by John Walker
  7.  
  8. */
  9.  
  10. #include "speakfree.h"
  11. #include "version.h"
  12.  
  13. #include <unistd.h>              /* For fork() and execvp() */
  14.  
  15. #ifdef AUDIO_DEVICE_FILE
  16. extern char *devAudioOutput,          /* Audio file names */
  17.         *devAudioControl;
  18. #endif
  19.  
  20. /*  Command names to run for mike and speaker.    The #ifdefs permit
  21.     you to override these at compile time.  */
  22.  
  23. #ifndef CMD_sfmike
  24. #define CMD_sfmike "sfmike"
  25. #endif
  26.  
  27. #ifndef CMD_sfspeaker
  28. #define CMD_sfspeaker "sfspeaker"
  29. #endif
  30.  
  31. static int debug = FALSE;          /* Debug flag */
  32. static char *progname;              /* Program name */
  33. static int cpmike;              /* Sfmike child process ID */
  34. static int nmike = 0;              /* Number of mike arguments */
  35. static char *cmdmike;              /* Command to launch sfmike */
  36. static char **argmike;              /* Sfmike command line arguments */
  37. static char file_descriptors[40];     /* -y option to pass file descriptors to sfmike and sfspeaker */
  38. static char newdest[256];          /* Destination for new connection */
  39.  
  40. /*  STRCMPCI  --  Case-insensitive string compare.  Roll our own
  41.           because the name of this function is notorious
  42.           for differing from one system to another.  */
  43.  
  44. static int strcmpci(a, b)
  45.   char *a, *b;
  46. {
  47.     char la[256], lb[256];
  48.     int i;
  49.  
  50.     for (i = 0; a[i] != 0; i++) {
  51.     la[i] = isupper(a[i]) ? tolower(a[i]) : a[i];
  52.     }
  53.     la[i] = 0;
  54.  
  55.     for (i = 0; b[i] != 0; i++) {
  56.     lb[i] = isupper(b[i]) ? tolower(b[i]) : b[i];
  57.     }
  58.     lb[i] = 0;
  59.  
  60. #ifdef DEBUG_STRCMPCI
  61.     fprintf(stderr, "strcmpci(<%s>, <%s>)\n", la, lb);
  62. #endif
  63.     return strcmp(la, lb);
  64. }
  65.  
  66. /*  PROG_NAME  --  Extract program name from argv[0].  */
  67.  
  68. static char *prog_name(arg)
  69.   char *arg;
  70. {
  71.     char *cp = strrchr(arg, '/');
  72.  
  73.     return (cp != NULL) ? cp + 1 : arg;
  74. }
  75.  
  76. /*  SHOWCMD  --  Show effective command line for program.  */
  77.  
  78. static void showcmd(progname, program, args)
  79.   char *progname;
  80.   char *program;
  81.   char **args;
  82. {
  83.     int i;
  84.  
  85.     fprintf(stderr, "%s: invoking %s as \"", progname, program);
  86.     for (i = 0; args[i] != NULL; i++) {
  87.     if (i > 0) {
  88.             fprintf(stderr, " ");
  89.     }
  90.         fprintf(stderr, "%s", args[i]);
  91.     }
  92.     fprintf(stderr, "\"\n");
  93. }
  94.  
  95. /*  STARTMIKE  --  Start sfmike in child process.  */
  96.  
  97. static void startMike()
  98. {
  99.     if (debug) {
  100.         showcmd(progname, "sfmike", argmike);
  101.     }
  102.     cpmike = fork();
  103.     if (cpmike == 0) {
  104.     if (debug) {
  105.             fprintf(stderr, "%s: launching \"sfmike%s%s\" in child process.\n",
  106.                 progname, file_descriptors[0] == 0 ? "" : " ", file_descriptors);
  107.     }
  108.     execvp(cmdmike, argmike);
  109.         perror("launching sfmike in child process");
  110.     exit(0);
  111.     } else if (cpmike == (pid_t) -1) {
  112.         perror("creating child process to run sfmike");
  113.     exit(1);
  114.     }
  115. }
  116.  
  117. /*  NEWCONNECTION  --  Prompt user for new connection.    If a
  118.                non-blank destination is entered,
  119.                startMike() is called to connect to it
  120.                and TRUE is returned.  A blank entry
  121.                indicates the user wants to quit and
  122.                returns FALSE.  */
  123.  
  124. static int newConnection()
  125. {
  126.     int i;
  127.  
  128.     printf("%s: New connection (return to exit)? ", progname);
  129.     fflush(stdout);
  130.     if (fgets(newdest, (sizeof newdest) - 2, stdin) != NULL) {
  131.     while ((strlen(newdest) > 0) &&
  132.            (isspace(newdest[strlen(newdest) - 1]))) {
  133.         newdest[strlen(newdest) - 1] = 0;
  134.     }
  135.     if (strlen(newdest) > 0) {
  136.         for (i = 1; i < nmike; i++) {
  137.                 if (*argmike[i] != '-') {
  138.             argmike[i] = newdest;
  139.             startMike();
  140.             return TRUE;
  141.         }
  142.         }
  143.     }
  144.     }
  145.     return FALSE;
  146. }
  147.  
  148. /*  USAGE  --  Print how-to-call information.  */
  149.  
  150. static void usage()
  151. {
  152.     V fprintf(stderr, "%s  --  Speak Freely launcher.\n", progname);
  153.     V fprintf(stderr, "              %s.\n", Relno);
  154.     V fprintf(stderr, "\n");
  155.     V fprintf(stderr, "Usage: %s [options] hostname[:port] [ file1 / . ]...\n", progname);
  156.     V fprintf(stderr, "Options: (* indicates defaults)\n");
  157.     V fprintf(stderr, "           -BOTH      Options for sfmike and sfspeaker follow\n");
  158.     V fprintf(stderr, "           -D         Enable debug output\n");
  159.     V fprintf(stderr, "         * -LAUNCH    Options for sflaunch follow\n");
  160.     V fprintf(stderr, "           -MIKE      Options for sfmike follow\n");
  161.     V fprintf(stderr, "           -Q         Quit when first connection ends\n");
  162.     V fprintf(stderr, "         * -SFLAUNCH  Options for sflaunch follow\n");
  163.     V fprintf(stderr, "           -SFMIKE    Options for sfmike follow\n");
  164.     V fprintf(stderr, "           -SFSPEAKER Options for sfspeaker follow\n");
  165.     V fprintf(stderr, "           -U         Print this message\n");
  166. #ifdef AUDIO_DEVICE_FILE
  167.     V fprintf(stderr, "           -Yaudiodev[:ctldev] Override default audio device file name(s)\n");
  168. #endif
  169.     V fprintf(stderr, "\n");
  170.     V fprintf(stderr, "by John Walker\n");
  171.     V fprintf(stderr, "   http://www.fourmilab.ch/\n");
  172. }
  173.  
  174. /*  Main program.  */
  175.  
  176. main(argc, argv)
  177.   int argc;
  178.   char *argv[];
  179. {
  180.     int cpspeaker, fdio, fdctl, i,
  181.     ndest = 0, nspeaker = 0, oneconnect = FALSE,
  182.     optmike = FALSE, optspeaker = FALSE, wpid, wstat;
  183.     char *cmdspeaker, *cp;
  184.     char **argspeaker;
  185.  
  186.     progname = prog_name(argv[0]);    /* Save program name */
  187.     file_descriptors[0] = 0;
  188.  
  189.     /*    Allocate argument arrays for sfmike and sfspeaker and
  190.     plug in always-supplied arguments.  */
  191.  
  192.     argmike = malloc((argc + 3) * sizeof (char *));
  193.     argspeaker = malloc((argc + 3) * sizeof(char *));
  194.     cmdmike = malloc(strlen(argv[0]) + 12);
  195.     cmdspeaker = malloc(strlen(argv[0]) + 12);
  196.  
  197.     if ((argmike == NULL) || (argspeaker == NULL) ||
  198.     (cmdmike == NULL) || (cmdspeaker == NULL)) {
  199.         fprintf(stderr, "%s: unable to allocate command and argument arrays for sfmike and sfspeaker.\n",
  200.         progname);
  201.     return 1;
  202.     }
  203.  
  204.     /*    Construct commands to invoke sfmike and sfspeaker.  As
  205.     long as argv[0] supplied a fully qualified path, we
  206.     execute them from the same directory from which sflaunch
  207.     was run.  */
  208.  
  209.     strcpy(cmdmike, argv[0]);
  210.     if ((cp = strrchr(cmdmike, '/')) == NULL) {
  211.     cp = cmdmike;
  212.     } else {
  213.     cp++;
  214.     }
  215.     strcpy(cp, CMD_sfmike);
  216.     argmike[nmike++] = cmdmike;
  217.  
  218.     strcpy(cmdspeaker, argv[0]);
  219.     if ((cp = strrchr(cmdspeaker, '/')) == NULL) {
  220.     cp = cmdspeaker;
  221.     } else {
  222.     cp++;
  223.     }
  224.     strcpy(cp, CMD_sfspeaker);
  225.     argspeaker[nspeaker++] = cmdspeaker;
  226.  
  227.     /*    Process command line options.  */
  228.  
  229.     for (i = 1; i < argc; i++) {
  230.     char *op, opt;
  231.  
  232.     op = argv[i];
  233.         if (*op == '-') {
  234.         opt = *(++op);
  235.  
  236.         /*    Check for options which direct those which follow
  237.         to sfmike, sfspeaker, both, or us.  Recognised options are
  238.         as follows with those on the same line equivalent:
  239.  
  240.             -both
  241.             -sfmike -mike
  242.             -sfspeaker -speaker
  243.             -sflaunch -launch
  244.         */
  245.  
  246.             if ((strcmpci(op, "both") == 0)) {
  247.         optmike = TRUE;
  248.         optspeaker = TRUE;
  249.         continue;
  250.         }
  251.  
  252.             if ((strcmpci(op, "mike") == 0) ||
  253.                 (strcmpci(op, "sfmike") == 0)) {
  254.         optmike = TRUE;
  255.         optspeaker = FALSE;
  256.         continue;
  257.         }
  258.  
  259.             if ((strcmpci(op, "speaker") == 0) ||
  260.                 (strcmpci(op, "sfspeaker") == 0)) {
  261.         optmike = FALSE;
  262.         optspeaker = TRUE;
  263.         continue;
  264.         }
  265.  
  266.             if ((strcmpci(op, "launch") == 0) ||
  267.                 (strcmpci(op, "sflaunch") == 0)) {
  268.         optmike = FALSE;
  269.         optspeaker = FALSE;
  270.         continue;
  271.         }
  272.  
  273.         /*    If this option is directed at sfmike or sfspeaker, append
  274.                 it to that program's argument vector.  */
  275.  
  276.         if (optmike) {
  277.         argmike[nmike++] = argv[i];
  278.         }
  279.         if (optspeaker) {
  280.         argspeaker[nspeaker++] = argv[i];
  281.         }
  282.         if (optmike || optspeaker) {
  283.         continue;
  284.         }
  285.  
  286.         if (islower(opt)) {
  287.         opt = toupper(opt);
  288.         }
  289.  
  290.         switch (opt) {
  291.                 case 'D':             /* -D  --  Debug output */
  292.             debug = TRUE;
  293.             break;
  294.  
  295.                 case 'Q':             /* -Q  --  Quit after first connection */
  296.             oneconnect = TRUE;
  297.             break;
  298.  
  299.                 case 'U':             /* -U  --  Print usage information */
  300.                 case '?':             /* -?  --  Print usage information */
  301.             usage();
  302.             return 0;
  303.  
  304. #ifdef AUDIO_DEVICE_FILE
  305.                 case 'Y':             /* -Yaudiodev:[ctldev] -- Specify audio
  306.                          I/O and control device file
  307.                          names. */
  308.             devAudioOutput = op + 1;
  309.                     if (strchr(op + 1, ':') != NULL) {
  310.                         devAudioControl = strchr(op + 1, ':') + 1;
  311.             }
  312.             break;
  313. #endif
  314.         }
  315.     } else {
  316.             argmike[nmike++] = op;    /* Plug destination into sfmike's argument list */
  317.         ndest++;
  318.     }
  319.     }
  320.  
  321.     if (ndest == 0) {
  322.         argmike[nmike++] = "X";       /* Placeholder for new connection */
  323.     }
  324.  
  325. #ifdef AUDIO_DEVICE_FILE
  326.     if (!soundinit(O_RDWR)) {
  327.         perror("opening sound device");
  328.         fprintf(stderr, "%s cannot open one or both of the audio and\n", progname);
  329.         fprintf(stderr, "audio control device files.  Be sure you specified\n");
  330.         fprintf(stderr, "the correct names for your system and audio drivers.\n");
  331.     exit(1);
  332.     }
  333.  
  334.     sound_open_file_descriptors(&fdio, &fdctl);
  335.  
  336.     if (debug) {
  337.         fprintf(stderr, "%s: Open file descriptors: audio = %d, control = %d\n",
  338.         progname, fdio, fdctl);
  339.     }
  340.     sprintf(file_descriptors, "-y#%d", fdio);
  341.     if (fdctl >= 0) {
  342.         sprintf(file_descriptors + strlen(file_descriptors), ":#%d", fdctl);
  343.     }
  344.  
  345.     /* Add file descriptor argument to mike and speaker argument lists, then
  346.        NULL terminate said lists. */
  347.  
  348.     argmike[nmike++] = file_descriptors;
  349.     argspeaker[nspeaker++] = file_descriptors;
  350. #endif
  351.  
  352.     argmike[nmike] = argspeaker[nspeaker] = NULL;
  353.  
  354.     if (debug) {
  355.         showcmd(progname, "sfspeaker", argspeaker);
  356.     }
  357.  
  358.     /* Fork child process and start sfspeaker, passing the file
  359.        descriptors for the audio and control files in via the
  360.        -y option. */
  361.  
  362.     cpspeaker = fork();
  363.     if (cpspeaker == 0) {
  364.     if (debug) {
  365.             fprintf(stderr, "%s: launching \"sfspeaker%s%s\" in child process.\n",
  366.                 progname, file_descriptors[0] == 0 ? "" : " ", file_descriptors);
  367.     }
  368.     execvp(cmdspeaker, argspeaker);
  369.         perror("launching sfspeaker in child process");
  370.     return 0;
  371.     } else if (cpspeaker == (pid_t) -1) {
  372.         perror("creating child process to run sfspeaker");
  373.     return 1;
  374.     }
  375.     free(argspeaker);
  376.  
  377.     /* Now that sfspeaker is running, we spawn another child process
  378.        to run sfmike, again invoked with the -y option to hand over
  379.        the file descriptors we've opened.  Why not launch sfmike in
  380.        the main process?  Because we need to wait on both processes
  381.        in order to avoid zombie processes and to kill sfspeaker
  382.        when the user quits sfmike. */
  383.  
  384.     if (ndest == 0) {
  385.     cpmike = -1;
  386.     if (!newConnection()) {
  387.         kill(cpspeaker, SIGHUP);
  388.     }
  389.     } else {
  390.     startMike();
  391.     }
  392.  
  393.     if (debug) {
  394.         fprintf(stderr, "%s: waiting on child processes for sfspeaker (%d) and sfmike (%d).\n",
  395.         progname, cpspeaker, cpmike);
  396.     }
  397.  
  398.     while (((cpmike != -1) || (cpspeaker != -1)) &&
  399.        ((wpid = wait(&wstat)) != -1)) {
  400.     if (debug) {
  401.             fprintf(stderr, "Child process %d (%s) terminated.\n", wpid,
  402.                 wpid == cpmike ? "sfmike" : (wpid == cpspeaker ? "sfspeaker" : "??unknown??"));
  403.     }
  404.  
  405.         /* The normal state of affairs is that we'll receive
  406.        notice of termination of sfmike first, when the user
  407.        ends the conversation.  But just in case sfspeaker
  408.        dies, we also shut down sfmike, since that makes it
  409.        easier for the user to restart the whole stack. */
  410.  
  411.     if (wpid == cpmike) {
  412.         cpmike = -1;          /* Mark mike exited */
  413.         if (!oneconnect && newConnection()) {
  414.         goto waiton;
  415.         }
  416.         if (cpspeaker != -1) {
  417.         kill(cpspeaker, SIGHUP);
  418.         }
  419.     } else {
  420.         cpspeaker = -1;          /* Mark speaker exited */
  421.         if (cpmike != -1) {
  422.                 fprintf(stderr, "%s: abnormal termination in sfspeaker.  Shutting down sfmike.\n",
  423.             progname);
  424.         kill(cpmike, SIGHUP);
  425.         } else {
  426.         if (debug) {
  427.                     fprintf(stderr, "%s: normal termination notification from sfspeaker.\n",
  428.             progname);
  429.         }
  430.         }
  431.     }
  432. waiton:;
  433.     }
  434.  
  435.     /* And now, with both child processes done, we can close the
  436.        audio device. */
  437.  
  438. #ifdef AUDIO_DEVICE_FILE
  439.     if (debug) {
  440.         fprintf(stderr, "%s: closing audio device.\n", progname);
  441.     }
  442.     soundterm();
  443. #endif
  444.  
  445.     return 0;
  446. }
  447.